#include "rsucomm.h"
#include <QTimer>
#include <QtEndian>

RsuComm::RsuComm(QObject *parent) : QObject(parent)
{
    m_nRsuType=RSU_TYPE_CHENGGU;
    m_bReadInBlockMode=false;

    connect(&m_tcpSocket, &QTcpSocket::connected,
            this, &RsuComm::socketConnected);
    connect(&m_tcpSocket, &QTcpSocket::disconnected,
            this, &RsuComm::socketDisconnected);
    // connect(&m_tcpSocket, &QTcpSocket::bytesWritten,
    //         this, &RsuComm::socketBytesWritten);
    connect(&m_tcpSocket, &QTcpSocket::readyRead,
            this, &RsuComm::socketReadyRead);
    connect(&m_tcpSocket, &QAbstractSocket::stateChanged,
             this, &RsuComm::stateChanged);
    connect(&m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(socketError(QAbstractSocket::SocketError)));
}

RsuComm::~RsuComm()
{
}

void RsuComm::openRsu(const QString &host, quint16 port)
{
    m_sHostAddr = host;
    m_nHostPort = port;
    tryReconnect();
}

void RsuComm::closeRsu()
{
    m_tcpSocket.close();
}

// 帧头
const static char *STX="\xFF\xFF";
// 帧尾
const static char *ETX="\xFF";
// 除去帧头、帧尾的最小帧长度：RSCTL(1) TYPE(1) BCC(1)
const static int MIN_FRAME_SIZE=3;
// 带帧头、帧尾的最小帧长度
const static int MIN_FRAME_SIZE_WITH_FRAME_HEAD_AND_END=6;

void RsuComm::socketConnected()
{
    logger()->debug(tr("<%1:%2>: connected.")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort));
}

void RsuComm::socketDisconnected()
{
    logger()->debug(tr("<%1:%2>: disconnected.")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort));
}

void RsuComm::socketBytesWritten(qint64 bytes)
{
    logger()->debug(tr("<%1:%2>: [%3] bytes written.")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(bytes));
}

void RsuComm::logBytesRead(const QByteArray &dataRead, const QString &flag)
{
    logger()->debug(tr("<%1:%2>: <<%3 [%4] bytes - [%5]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(flag)
                    .arg(dataRead.length())
                    .arg(dataRead.toHex().data()));
}

void RsuComm::socketReadyRead()
{
    if (m_bReadInBlockMode)
    {
        return;
    }

    QByteArray dataRead=m_tcpSocket.readAll();
    if (dataRead.length()==0)
    {
        logger()->info(tr("<%1:%2>: 0 byte read, disconnect.")
                       .arg(m_sHostAddr)
                       .arg(m_nHostPort));
        m_tcpSocket.disconnectFromHost();
        return;
    }
    processDataRead(dataRead, NULL, 0);
}

bool RsuComm::processDataRead(const QByteArray &dataRead, QByteArray *pExpectedFrame, quint8 nExpectedFrameType)
{
    m_dataRead.append(dataRead);

    bool bGotExpectedFrame=false;
    while (m_dataRead.length()>MIN_FRAME_SIZE_WITH_FRAME_HEAD_AND_END)
    {
        int start = m_dataRead.indexOf(STX, 0);
        if (start<0)
        {
            // 没有包头，丢弃所有数据
            logger()->debug(tr("<%1:%2>: no frame head, discard %3 bytes.")
                            .arg(m_sHostAddr)
                            .arg(m_nHostPort)
                            .arg(m_dataRead.length()));
            m_dataRead.clear();
            return false;
        }
        int end = m_dataRead.indexOf(ETX, strlen(STX));
        if (end<0)
        {
            // 没有包尾，等待再次接收数据
            return false;
        }
        // 抽取去除包头、包尾的帧数据
        QByteArray frame = m_dataRead.mid(start+strlen(STX), end-start-strlen(STX));
        // 从读入缓存中去除本帧数据（连同包头、包尾）
        m_dataRead.remove(0, end+strlen(ETX));
        if (processRawFrame(frame, pExpectedFrame, nExpectedFrameType))
        {
            bGotExpectedFrame=true;
        }
    }
    return bGotExpectedFrame;
}

void RsuComm::mockRawFrameData(QByteArray &frame)
{
    processRawFrame(frame, NULL, 0);
}

bool RsuComm::processRawFrame(QByteArray &frame, QByteArray *pExpectedFrame, quint8 nExpectedFrameType)
{
    // 长度检查
    if (frame.length()<MIN_FRAME_SIZE)
    {
        // 长度不足，丢弃帧数据
        logger()->debug(tr("<%1:%2>: too short, discard frame[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(frame.toHex().data()));
        return false;
    }

    // BCC校验
    quint8 bcc = getXOR(frame);
    if (0!=bcc)
    {
        // BCC校验失败，丢弃帧数据
        logger()->debug(tr("<%1:%2>: bcc error, discard frame[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(frame.toHex().data()));
        return false;
    }
    frame.replace("\xFE\x01", 2, "\xFF", 1);
    frame.replace("\xFE\x00", 2, "\xFE", 1);

    logger()->debug(tr("<%1:%2>: frame[%3]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(frame.toHex().data()));

    quint8 nFrameIndex=frame[0];
    quint8 nFrameType=frame[1];
    // 去除帧序号和帧类型
    frame.remove(0, 2);
    // 去除BCC
    frame.remove(frame.length()-1, 1);
    if (NULL!=pExpectedFrame)
    {
        bool bRlt=(nFrameType==nExpectedFrameType);
        if (bRlt)
        {
            pExpectedFrame->clear();
            pExpectedFrame->append(frame);
        }
        return bRlt;
    }
    else
    {
        processFrame(nFrameIndex, nFrameType, frame);
        return false;
    }
}

quint8 RsuComm::getXOR(const QByteArray &data)
{
    quint8 n=0x00;
    for(int it=0; it<data.length(); ++it)
    {
        n ^= data[it];
    }
    return n;
}

void RsuComm::processFrame(quint8 nFrameIndex, quint8 nFrameType, QByteArray &frame)
{
    emit newFrameArrived(nFrameIndex, nFrameType, frame);
}

void RsuComm::socketStateChanged(QAbstractSocket::SocketState socketState)
{
    logger()->debug(tr("<%1:%2>: Socket状态变化[%3]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(socketState));
}

void RsuComm::socketError(QAbstractSocket::SocketError socketError)
{
    logger()->debug(tr("<%1:%2>: Socket错误[%3][%4]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(socketError)
                    .arg(this->m_tcpSocket.errorString()));
    QTimer::singleShot(10000, this, &RsuComm::tryReconnect);

}

void RsuComm::tryReconnect()
{
    if (QAbstractSocket::UnconnectedState==m_tcpSocket.state())
    {
        m_tcpSocket.connectToHost(m_sHostAddr, m_nHostPort);
    }
}

void RsuComm::wrapFrame(QByteArray &data)
{
    data.replace("\xFE", 1, "\xFE\x00", 2);
    data.replace("\xFF", 1, "\xFE\x01", 2);
    quint8 rsctl = getRSCTL();
    data.prepend(rsctl);
    quint8 bcc = getXOR(data);
    data.append(bcc);
    data.prepend(STX);
    data.append(ETX);
}

int RsuComm::wrapAndSendFrame(QByteArray &data)
{
    if (char(0xF1)!=data[0])
    {
        logger()->debug(tr("<%1:%2>: command[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(data.toHex().data()));
    }

    wrapFrame(data);

    qint64 nWrittenDataLen=m_tcpSocket.write(data);
    if (nWrittenDataLen!=data.length())
    {
        // Note: 20151221使用成谷天线控制盒测试发现：
        //  如果天线电源被关闭，write仍返回成功，直到约3分钟后才有
        // socketStateChanged等事件，所以这个判断条件基本不会被走到！
        logger()->debug("m_tcpSocket.write failure");
        return ERR_RSU_OTHER;
    }
    return ERR_SUCCESS;
}

#define PRE_CHECK_FOR_RQ \
    if (QAbstractSocket::ConnectedState!=this->m_tcpSocket.state()) \
    { \
        return ERR_RSU_NOT_OPENED; \
    }

#define PRE_CHECK_FOR_RS PRE_CHECK_FOR_RQ

int RsuComm::readAllAndUnwrapFrame(QByteArray *pExpectedFrame,
                                   quint8 nExpectedFrameType, int nTimeOut)
{
    PRE_CHECK_FOR_RS;

    m_bReadInBlockMode=true;
    bool bWaitRlt=m_tcpSocket.waitForReadyRead(nTimeOut);
    QByteArray dataRead;
    if (bWaitRlt)
    {
        dataRead=m_tcpSocket.readAll();
    }
    m_bReadInBlockMode=false;

    if (!bWaitRlt)
    {
        if (nExpectedFrameType!=0xE1)
        {
            logger()->info("read timeout, TimeOut=[%1]", nTimeOut);
        }
        return ERR_RSU_OVERTIME;
    }

    if (processDataRead(dataRead, pExpectedFrame, nExpectedFrameType))
    {
        return ERR_SUCCESS;
    }
    else
    {
        return ERR_RSU_OVERTIME;
    }
}

int RsuComm::initRsuRq(char *Time, int BSTInterval, int TxPower, int PLLChannelID)
{
    PRE_CHECK_FOR_RQ;

    /*

    深圳成谷通讯数据示例：
    char peer0_0[] = {
    0xff, 0xff, 0x80, 0xf0, 0x56, 0x6d, 0xfe, 0x01,
    0xb8, 0x05, 0x0a, 0x00, 0x03, 0xff };

    char peer1_0[] = {
    0xff, 0xff, 0x08, 0xe0, 0x00, 0x06, 0x12, 0x00,
    0x20, 0x00, 0x30, 0x33, 0xdf, 0xff };

    */

    QByteArray data;
    data.append(0xF0);
    data.append(Time, 4);
    data.append(quint8(BSTInterval));
    data.append(quint8(TxPower));
    data.append(quint8(PLLChannelID));
    return wrapAndSendFrame(data);
}

quint8 RsuComm::transBaudToCode(long baud)
{
    quint8 b=0;
    if (9600==baud)
    {
        b=1;
    }
    else if (38400==baud)
    {
        b=2;
    }
    else if (57600==baud)
    {
        b=3;
    }
    else if (115200==baud)
    {
        b=4;
    }

    return b;
}

/*!
 * \brief PSAM通道复位
 * \param PSAMSlot: 卡槽号, 0x00-0x05
 * \param baud: PSAM通信速率, 1-9600; 2-38400; 3-57600; 4-115200。
 * 2011第13号公告表7-58与《GBT_28423-2012_电子收费_路侧单元与车道控制器接口.pdf》表59不同，一个是1字节，另一个是4字节。
 */
int RsuComm::resetPsamRq(int PSAMSlot, long baud)
{
    PRE_CHECK_FOR_RQ;

    /*

    深圳成谷通讯数据示例：

    char peer0_1[] = {
    0xff, 0xff, 0x80, 0xf8, 0x00, 0x00, 0x78, 0xff };

    char peer1_1[] = {
    0xff, 0xff, 0x08, 0xe8, 0x00, 0x0e, 0x3b, 0x79,
    0x18, 0x00, 0x00, 0x50, 0x53, 0x4d, 0x5f, 0x54,
    0x43, 0x31, 0x30, 0x30, 0x83, 0xff };

    */

    QByteArray data;
    data.append(0xF8);
    data.append(quint8(PSAMSlot));
    // NOTE: 实际测试使用1-4貌似有问题
    Q_UNUSED(baud);
    quint8 b=0; // transBaudToCode(baud);
    data.append(b);
    return wrapAndSendFrame(data);
}

int RsuComm::resetPsamRs(int PSAMSlot, int *rlen, char *PSAMNo)
{
    // NOTE: 返回数据中貌似没有卡槽号信息，无法校验回应信息是否与发送值一致。
    Q_UNUSED(PSAMSlot);

    QByteArray frame;
    int nRlt = readAllAndUnwrapFrame(&frame, 0xE8, 80);
    /*
     *
    深圳成谷通讯数据示例（不含帧头、帧序号、帧类型、校验码、帧尾）：
    00 0e 3b 79 18 00 00 50 53 4d 5f 54 43 31 30 30
    ss ll pp .. .. .. .. .. .. .. .. .. .. .. .. pp

    ss: status
    ll: 随后字节的长度，此处0e表示14字节
    pp: psam info
    */

    if (nRlt!=ERR_SUCCESS)
    {
        return nRlt;
    }
    if (frame.length()<2)
    {
        return ERR_RSU_OTHER;
    }
    quint8 status = frame[0];
    if (0!=status)
    {
        *rlen=0;
        return -102;
    }
    *rlen = frame[1];
    if (frame.length()<(2+*rlen))
    {
        return ERR_RSU_OTHER;
    }
    memcpy(PSAMNo, frame.data()+2, *rlen);
    return nRlt;
}

int RsuComm::psamChannelRq(int PSAMSlot, int APDUList, char *APDU)
{
    PRE_CHECK_FOR_RQ;

    /*

    深圳成谷通讯数据示例：

    PSAM_CHANNEL(PSAMSlot,2,<\x07\x00\xA4\x00\x00\x02\x3F\x00><\x05\x00\xB0\x96\x00\x06>,TimeOut)

    char peer0_3[] = {
    0xff, 0xff, 0x80, 0xf9, 0x00, 0x02, 0x07, 0x00,
    0xa4, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x05, 0x00,
    0xb0, 0x96, 0x00, 0x06, 0xc0, 0xff };

    char peer1_3[] = {
    0xff, 0xff, 0x08, 0xe9, 0x00, 0x02, 0x19, 0x6f,
    0x15, 0x84, 0x0e, 0x31, 0x50, 0x41, 0x59, 0x2e,
    0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30,
    0x31, 0xa5, 0x03, 0x88, 0x01, 0x01, 0x90, 0x00,
    0x08, 0x01, 0x63, 0x00, 0x01, 0x50, 0x83, 0x90,
    0x00, 0xfb, 0xff };

    */

    QByteArray data;
    data.append(0xF9);
    data.append(quint8(PSAMSlot));
    data.append(quint8(APDUList));
    data.append(APDU, getCmdGroupLen(APDUList, APDU));
    return wrapAndSendFrame(data);
}

int RsuComm::psamChannelRs(int PSAMSlot, int *APDUList, char *Data)
{
    // TODO: 需要校验回应信息的卡槽号是否与发送值一致。
    Q_UNUSED(PSAMSlot);

    QByteArray frame;
    int nRlt = readAllAndUnwrapFrame(&frame, 0xE9, 50);
    if (nRlt!=ERR_SUCCESS)
    {
        return nRlt;
    }
    if (frame.length()<(MIN_FRAME_SIZE+2))
    {
        return ERR_RSU_OTHER;
    }
    quint8 status = frame[2];
    if (0!=status)
    {
        *APDUList=0;
        return -1012;
    }
    *APDUList = frame[3];
    int leftLen=frame.length()-MIN_FRAME_SIZE-2;
    if (leftLen<=0)
    {
        return ERR_RSU_OTHER;
    }
    memcpy(Data, frame.data()+4, leftLen);
    return nRlt;
}

int RsuComm::rsuInfoRq()
{
    PRE_CHECK_FOR_RQ;

    /*

    深圳成谷通讯数据示例：

    char peer0_2[] = {
    0xff, 0xff, 0x80, 0xf0, 0x01, 0x71, 0xff };

    char peer1_2[] = {
    0xff, 0xff, 0x08, 0xe0, 0x01, 0x06, 0x12, 0x00,
    0x20, 0x00, 0x30, 0x33, 0xde, 0xff };

    */

    QByteArray data;
    data.append(0xF7);
    return wrapAndSendFrame(data);
}

int RsuComm::initPrimRq(char *BeaconID, char *Time, int Profile, int MandApplicationlist, char *MandApplication, int Profilelist)
{
    PRE_CHECK_FOR_RQ;

    /*

    深圳成谷通讯数据示例：

    INITIALISATION(Rand,Now,0,1,<\x07\x41\x87\x29\x20\x1A\x00\x2B>,0,TimeOut)

    char peer0_5[] = {
    0xff, 0xff, 0x80, 0xf1, 0xfe, 0x01, 0xfe, 0x01,
    0xfe, 0x01, 0xfe, 0x01, 0x50, 0x03, 0x91, 0xc0,
    0x05, 0x00, 0x00, 0x29, 0x56, 0x6d, 0xfe, 0x01,
    0xb9, 0x00, 0x01, 0x41, 0x87, 0x29, 0x20, 0x1a,
    0x00, 0x2b, 0x00, 0xdd, 0xff };

    char peer1_5[] = {
    0xff, 0xff, 0x08, 0xe1, 0x00, 0x02, 0xd4, 0x15,
    0xf0, 0xc0, 0x03, 0x91, 0xd0, 0x00, 0x01, 0xc1,
    0x01, 0x20, 0x27, 0xc7, 0xe0, 0xba, 0xa3, 0x63,
    0x01, 0x00, 0x01, 0x00, 0x00, 0x63, 0x01, 0x13,
    0x02, 0x01, 0x00, 0x01, 0x38, 0x20, 0x23, 0x07,
    0x17, 0x20, 0x23, 0x07, 0x17, 0x28, 0xc9, 0xbd,
    0xb6, 0xab, 0x37, 0x01, 0x00, 0x01, 0x16, 0x10,
    0x37, 0x02, 0x13, 0x03, 0x22, 0x01, 0x00, 0x27,
    0x56, 0x01, 0x20, 0x14, 0x09, 0x11, 0x20, 0x04,
    0x09, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x02, 0xd4, 0x15, 0xf0, 0x10, 0x23, 0x00,
    0x9e, 0xff };

    */


    // INITIALISATION(Rand,Now,0,1,<\x07\x41\x87\x29\x20\x1A\x00\x2B>,0,TimeOut)
    /*
     *
    深圳成谷通讯数据示例：
    ff ff ff ff 50 03 91 c0 05 00 00 29 54 cf 63 01 00 01 41 87 29 20 1a 00 2b 00
    ff ff ff ff 50 03 91 c0 05 00 48 23 54 cf 63 04 00 01 41 87 29 20 1a 00 2b 00
    ff ff ff ff 50 03 91 c0 05 00 18 be 54 cf 63 07 00 01 41 87 29 20 1a 00 2b 00
                            bb bb bb bb tt tt tt tt pp ll aa aa aa aa aa aa aa pp

    上海搜林通讯数据示例：
    91 c0 05 00 18 be 54 cf 63 07 00 01 41 87 29 20 1a 00 2b 00
          bb bb bb bb tt tt tt tt pp ll aa aa aa aa aa aa aa pp

        bb: BeaconID
        tt: Time
        pp: profile
        ll: MandApplicationlist
        aa: MandApplication
        pp: Profilelist
    */
    QByteArray data;
    data.append(0xF1);

    if (RSU_TYPE_CHENGGU==m_nRsuType)
    {
        // LID
        data.append("\xFF\xFF\xFF\xFF", 4);
        // 含义未知
        data.append("\x50\x03", 2);
    }
    // PDU号码，不得设定到 0000 或 0001
    quint8 pdu=0x02;
    // 分段字头，在pdu=0x02时，应该为0x91
    quint8 head = 0x81 | (pdu<<3);
    data.append(head);
    // INITIALIZATION.request，按GBT-20851.3-2007附录B
    data.append(0xC0);
    data.append(BeaconID, 4);
    data.append(Time, 4);
    data.append(quint8(Profile));
    data.append(quint8(MandApplicationlist));
    // 不知道为何要去掉本条命令的长度（只有一条命令）
    data.append(MandApplication+1, getCmdGroupLen(MandApplicationlist, MandApplication)-1);
    data.append(quint8(Profilelist));
    return wrapAndSendFrame(data);
}

int RsuComm::getSecureRq(int AccessCredentialsOp, int Mode,
                         int DID, char *AccessCredentials,
                         int KeyIdForEncryptOp, int FID, int Offset, int Length,
                         char *RandRSU, int KeyIdForAuthen, int KeyIdForEncrypt)
{
    Q_UNUSED(AccessCredentialsOp);
    Q_UNUSED(Mode);
    Q_UNUSED(AccessCredentials);
    Q_UNUSED(FID);

    /*

    深圳成谷通讯数据示例：

    GetSecure(0,1,1,\x00\x00\x00\x00\x00\x00\x00\x00,1,1,0,16,\x11\x22\x33\x44\x55\x66\x77\x88,0,0,TimeOut)

    char peer0_6[] = {
    0xff, 0xff, 0x80, 0xf2, 0x91, 0x05, 0x01, 0x00,
    0x14, 0x80, 0x01, 0x00, 0x00, 0x10, 0x11, 0x22,
    0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x00, 0x00,
    0xea, 0xff };

    char peer1_6[] = {
    0xff, 0xff, 0x08, 0xe2, 0x00, 0x91, 0x18, 0x01,
    0x15, 0x01, 0x8c, 0xc6, 0x84, 0xad, 0xfe, 0x01,
    0xd5, 0x10, 0x61, 0x3a, 0xc5, 0x77, 0x3b, 0xbe,
    0x58, 0xd8, 0x88, 0x9b, 0x07, 0x86, 0xe6, 0x53,
    0x0c, 0x94, 0xa0, 0x00, 0x4f, 0x79, 0xa9, 0x37,
    0x28, 0x4e, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x25, 0xff };

    */

    /*
     *
    Action-Request::=SEQUENCE{
        mode             	BOOLEAN,
        did                	Dsrc-DID,
        actionType         	ActionType,
        accessCredentials   OCTET STRING (SIZE(0..127,...)) OPTIONAL,
        actionParameter    	Container OPTIONAL,
        iid                 Dsrc-DID OPTIONAL
        }

    GetSecure(0,1,1,\x00\x00\x00\x00\x00\x00\x00\x00,1,1,0,16,\x11\x22\x33\x44\x55\x66\x77\x88,0,0,TimeOut)

    ff ff 80 f2 91 05 01 00 14 80 01 00 00 10 11 22 33 44 55 66 77 88 00 00 ea ff
                      dd aa       kk    oo ll rr rr rr rr rr rr rr rr aa ee
    */

    QByteArray data;
    data.append(0xF2);
    // 含义未知
    data.append("\x91\x05", 2);
    // did? - 01
    data.append(quint8(DID));
    quint8 actionType=0x00;
    data.append(actionType);
    // 含义未知
    data.append("\x14\x80", 2);
    // KeyIdForEncryptOp? - 01
    data.append(quint8(KeyIdForEncryptOp));
    // 含义未知
    quint8 tmp = 0x00;
    data.append(tmp);
    data.append(quint8(Offset));
    data.append(quint8(Length));
    data.append(RandRSU, 8);
    data.append(quint8(KeyIdForAuthen));
    data.append(quint8(KeyIdForEncrypt));
    return wrapAndSendFrame(data);
}

int RsuComm::getSecureRs(int *DID, int *FID, int *Length, char *File, char *Authenticator, int *ReturnStatus)
{
    /*
     *
    Action-Response::=SEQUENCE{
        fill                BIT STRING (SIZE(2)),
        did                	Dsrc-DID,
        responseParameter  	Container OPTIONAL,
        iid                 Dsrc-DID OPTIONAL,
        ret                 ReturnStatus
        }
    GetSecure_rs(1, 1, 32, 3f899f06b60e76061da17183c618922f0b3030291d2037b3df6468bed383ef1e, 0000000000000000, 0, 1000): 0

    ff ff 08 e2 00 91 18 01 15 01 3f 89 9f 06 b6 0e 76 06 1d a1 71 83 c6 18 92 2f 0b 30 30 29 1d 20 37 b3 df 64 68 be d3 83 ef 1e 00 00 00 00 00 00 00 00 00 eb ff
    ff ff 08 e2 00 91 18 01 15 01 3f 89 9f 06 b6 0e 76 06 1d a1 71 83 c6 18 92 2f 0b 30 30 29 1d 20 37 b3 df 64 68 be d3 83 ef 1e 00 00 00 00 00 00 00 00 00 eb ff
                                  ff...                                                                                     ...ff aa aa aa aa aa aa aa aa ss
     */

    QByteArray frame;
    int nRlt = 0; // readAllAndUnwrapFrame(frame, 0xE2);
    if (nRlt!=ERR_SUCCESS)
    {
        return nRlt;
    }
    // 6 + File(n) + Authenticator(8) + ReturnStatus(1)
    if (frame.length()<(MIN_FRAME_SIZE+15))
    {
        return ERR_RSU_OTHER;
    }
    int index=2;

    index += 3;     // 00 91 18?
    *DID = frame[index];
    index++;

    index++;        // 15?
    *FID = frame[index];
    index++;

    *Length = frame.length()-index-8-1-1;
    if (*Length<=0)
    {
        return ERR_RSU_OTHER;
    }
    memcpy(File, frame.data()+index, *Length);
    index += *Length;

    memcpy(Authenticator, frame.data()+index, 8);
    index += 8;

    *ReturnStatus = frame[index];

    return nRlt;
}

int RsuComm::transChannelRq(int Mode, int DID, int ChannelID, int APDULIST, char *APDU)
{
    Q_UNUSED(Mode);
    Q_UNUSED(ChannelID);

    /*
     *
    Action-Request::=SEQUENCE{
        mode             	BOOLEAN,
        did                	Dsrc-DID,
        actionType         	ActionType,
        accessCredentials   OCTET STRING (SIZE(0..127,...)) OPTIONAL,
        actionParameter    	Container OPTIONAL,
        iid                 Dsrc-DID OPTIONAL
        }

    TransferChannel_rq(1, 1, 1, 2, 05805c0002040500b201cc2b, 1000)
    ff ff 80 f2 91 05 01 03 18 01 02 05 80 5c 00 02 04 05 00 b2 01 cc 2b 71  ff
                         tt       ll aa...                         ...aa
     */

    QByteArray data;
    data.append(0xF2);
    // 含义未知
    data.append("\x91\x05", 2);
    // did? - 01
    data.append(quint8(DID));
    quint8 actionType=0x03;
    data.append(actionType);
    // 含义未知
    data.append("\x18\x01", 2);

    data.append(quint8(APDULIST));
    data.append(APDU, getCmdGroupLen(APDULIST, APDU));
    return wrapAndSendFrame(data);
}

int RsuComm::transChannelRs(int *DID, int *ChannelID, int *APDULIST, char *Data,
                           int *ReturnStatus)
{
    /*
     *
    Action-Response::=SEQUENCE{
        fill                BIT STRING (SIZE(2)),
        did                	Dsrc-DID,
        responseParameter  	Container OPTIONAL,
        iid                 Dsrc-DID OPTIONAL,
        ret                 ReturnStatus
        }

    TransferChannel_rs(1, 1, 2, 060000000090002daa2900000000000000000000000000000000000000000000000000000000000000000000000000000000009000, 0, 1000): 0
    ff ff 08 e2 00 91 18 01 19 01 02 06 00 00 00 00 90 00 2d aa 29 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 d0 ff
                                  ll dd...                                                                                                                                                    ...dd ss
     */

    QByteArray frame;
    int nRlt = 0; // readAllAndUnwrapFrame(frame, 0xE2);
    if (nRlt!=ERR_SUCCESS)
    {
        return nRlt;
    }
    // 6 + File(n) + Authenticator(8) + ReturnStatus(1)
    if (frame.length()<(MIN_FRAME_SIZE+15))
    {
        return ERR_RSU_OTHER;
    }
    int index=2;

    index += 3;     // 00 91 18?
    *DID = frame[index];
    index++;

    index++;        // 19?
    *ChannelID = frame[index];
    index++;

    *APDULIST = frame[index];
    index++;

    int len = frame.length()-index-1-1;
    if (len<=0)
    {
        return ERR_RSU_OTHER;
    }
    memcpy(Data, frame.data()+index, len);
    index += len;

    *ReturnStatus = frame[index];

    return nRlt;
}

int RsuComm::setMmiRq(int Mode, int DID, int SetMMIPara)
{
    // TODO:
    Q_UNUSED(Mode);
    Q_UNUSED(DID);
    Q_UNUSED(SetMMIPara);

    return -1;
}

int RsuComm::setMmiRs(int *DID, int *ReturnStatus)
{
    // TODO:
    Q_UNUSED(DID);
    Q_UNUSED(ReturnStatus);

    return -1;
}

int RsuComm::getSecureTransChannelRq(int AccessCredentialsOp, int G_mode, int G_DID,
                                     char *AccessCredentials, int KeyIdForEncryptOp,
                                     int FID, int Offset, int Length,
                                     char *RandRSU, int KeyIdForAuthen, int KeyIdForEncrypt,
                                     int T_mode, int T_DID, int ChannelID,
                                     int APDULIST, char *APDU)
{
    // TODO:
    Q_UNUSED(AccessCredentialsOp);
    Q_UNUSED(G_mode);
    Q_UNUSED(G_DID);
    Q_UNUSED(AccessCredentials);
    Q_UNUSED(KeyIdForEncryptOp);
    Q_UNUSED(FID);
    Q_UNUSED(Offset);
    Q_UNUSED(Length);
    Q_UNUSED(RandRSU);
    Q_UNUSED(KeyIdForAuthen);
    Q_UNUSED(KeyIdForEncrypt);
    Q_UNUSED(T_mode);
    Q_UNUSED(T_DID);
    Q_UNUSED(ChannelID);
    Q_UNUSED(APDULIST);
    Q_UNUSED(APDU);

    return -1;
}

int RsuComm::getSecureTransChannelRs(int *G_DID, int *FID, int *Length, char *File,
                                     char *Authenticator, int *G_ReturnStatus,
                                     int *T_DID, int *ChannelID, int *APDULIST,
                                     char *Data, int *T_ReturnStatus)
{
    // TODO:
    Q_UNUSED(G_DID);
    Q_UNUSED(FID);
    Q_UNUSED(Length);
    Q_UNUSED(File);
    Q_UNUSED(Authenticator);
    Q_UNUSED(G_ReturnStatus);
    Q_UNUSED(T_DID);
    Q_UNUSED(ChannelID);
    Q_UNUSED(APDULIST);
    Q_UNUSED(Data);
    Q_UNUSED(T_ReturnStatus);

    return -1;
}

int RsuComm::transChannelSetMmiRq(int T_mode, int T_DID, int ChannelID,
                                  int APDULIST, char *APDU, int S_mode,
                                  int S_DID, int SetMMIPara)
{
    Q_UNUSED(T_mode);
    Q_UNUSED(ChannelID);
    Q_UNUSED(S_mode);

    /*
     *
    Action-Request::=SEQUENCE{
        mode             	BOOLEAN,
        did                	Dsrc-DID,
        actionType         	ActionType,
        accessCredentials   OCTET STRING (SIZE(0..127,...)) OPTIONAL,
        actionParameter    	Container OPTIONAL,
        iid                 Dsrc-DID OPTIONAL
        }

    拼接指令，由TransferChannel和SetMMI拼接而成
    TransferChannelSetMMI_rq(1, 1, 1, 1, 05805c000204, 1, 1, 0, 1000)
    ff ff 80 f2 91 05 01 03 18 01 01 05 80 5c 00 02 04 91 05 01 04 1a 00 a8 ff
                         tt       ll aa...       ...aa          tt
     */

    QByteArray data;
    data.append(0xF2);
    // 含义未知
    data.append("\x91\x05", 2);
    // did? - 01
    data.append(quint8(T_DID));
    quint8 actionType=0x03;
    data.append(actionType);
    // 含义未知
    data.append("\x18\x01", 2);

    data.append(quint8(APDULIST));
    data.append(APDU, getCmdGroupLen(APDULIST, APDU));

    // 含义未知
    data.append("\x91\x05", 2);
    // did? - 01
    data.append(quint8(S_DID));
    actionType=0x04;
    data.append(actionType);
    // 含义未知
    data.append("\x1A", 1);
    data.append(quint8(SetMMIPara));

    return wrapAndSendFrame(data);
}

int RsuComm::transChannelSetMmiRs(int *T_DID, int *ChannelID, int *APDULIST, char *Data, int *T_ReturnStatus, int *S_DID, int *S_ReturnStatus)
{
    /*
     *
    Action-Response::=SEQUENCE{
        fill                BIT STRING (SIZE(2)),
        did                	Dsrc-DID,
        responseParameter  	Container OPTIONAL,
        iid                 Dsrc-DID OPTIONAL,
        ret                 ReturnStatus
        }

    TransferChannelSetMMI_rs(1, 1, 1, 06000000009000, 0, 1): 0
    ff ff 08 e2 00 91 18 01 19 01 01 06 00 00 00 00 90 00 00 91 18 01 1b 00 7e ff
                                  ll dd...          ...dd ss          ?? ss
     */

    QByteArray frame;
    int nRlt = 0; // readAllAndUnwrapFrame(frame, 0xE2);
    if (nRlt!=ERR_SUCCESS)
    {
        return nRlt;
    }
    // 6 + APDULIST(1) + Data(n) + T_ReturnStatus(1) + 4 + S_ReturnStatus(1)
    if (frame.length()<(MIN_FRAME_SIZE+13))
    {
        return ERR_RSU_OTHER;
    }
    int index=2;

    index += 3;     // 00 91 18?
    *T_DID = frame[index];
    index++;

    index++;        // 19?
    *ChannelID = frame[index];
    index++;

    *APDULIST = frame[index];
    index++;

    int len = frame.length()-index-1-5-1;
    if (len<=0)
    {
        return ERR_RSU_OTHER;
    }
    memcpy(Data, frame.data()+index, len);
    index += len;

    *T_ReturnStatus = frame[index];
    index++;

    index += 2;     // 91 18?
    *S_DID = frame[index];
    index++;

    index++;        // 1b?

    *S_ReturnStatus = frame[index];

    return nRlt;
}

/*!
 * \brief 《GBT_28421-2012_电子收费_基于专用短程通信的电子收费交易.pdf》 7.10.2 Event-Report-Request
 */
int RsuComm::reportEventRq(quint32 nMacId, int Mode, int DID, int EventType)
{
    PRE_CHECK_FOR_RQ;

    Q_UNUSED(Mode);

    /*

    深圳成谷通讯数据示例：

    EVENT_REPORT(0,1,0,TimeOut)

    char peer0_13[] = {
    0xff, 0xff, 0x80, 0xf3, 0x02, 0xd4, 0x15, 0xf0,
    0x40, 0x03, 0x91, 0x60, 0x01, 0x00, 0xf3, 0xff };

    */


    /*

    Event-Report-Request::=SEQUENE{
        mode        Boolean,
        did         DirectoryID,
        eventType   EventType,
        ...         ... OPTIONAL
    }

    深圳成谷通讯数据示例，EVENT_REPORT_rq(0, 1, 0, 1000)：
    ff ff 80 f3 02 ca 21 2f 40 03 91 60 01 00 06 ff
                mm mm mm mm             dd ee
     */

    QByteArray data;
    data.append(0xF3);
    // MacId
    quint32 nTmp=qFromBigEndian(nMacId);
    char szBuf[8];
    memcpy(szBuf, &nTmp, 4);
    data.append(szBuf, 4);
    // 含义未知
    data.append("\x40\x03\x91\x60", 4);
    data.append(quint8(DID));
    data.append(quint8(EventType));
    return wrapAndSendFrame(data);
}
